package encryptHelper

import (
	"bytes"
	"crypto/cipher"
	"encoding/base64"
	"fmt"
	"golang.org/x/crypto/blowfish"
)

func BlowfishECBEncrypt(src, key string) string {
	block, err := blowfish.NewCipher([]byte(key))
	if err != nil {
		panic(err)
	}
	ecb := NewECBEncrypter(block)
	content := []byte(src)
	content = PKCS5Padding(content, block.BlockSize())
	crypted := make([]byte, len(content))
	ecb.CryptBlocks(crypted, content)
	return base64.StdEncoding.EncodeToString(crypted)
}

func BlowfishECBDecrypt(data string, key string) (string, error) {
	crypted, err := base64.StdEncoding.DecodeString(string(data))
	if err != nil {
		return "", err
	}
	block, err := blowfish.NewCipher([]byte(key))
	if err != nil {
		return "", err
	}
	blockMode := NewECBDecrypter(block)
	origData := make([]byte, len(crypted))
	blockMode.CryptBlocks(origData, crypted)
	origData = PKCS5UnPadding(origData)
	return string(origData), nil
}

type ECB struct {
	b         cipher.Block
	blockSize int
}

func NewECB(b cipher.Block) *ECB {
	return &ECB{
		b:         b,
		blockSize: b.BlockSize(),
	}
}

type ECBEncrypter ECB

// NewECBEncrypter returns a BlockMode which encrypts in electronic code book
// mode, using the given Block.
func NewECBEncrypter(b cipher.Block) cipher.BlockMode {
	return (*ECBEncrypter)(NewECB(b))
}
func (x *ECBEncrypter) BlockSize() int { return x.blockSize }
func (x *ECBEncrypter) CryptBlocks(dst, src []byte) {
	if len(src)%x.blockSize != 0 {
		panic("crypto/cipher: input not full blocks")
	}
	if len(dst) < len(src) {
		panic("crypto/cipher: output smaller than input")
	}
	for len(src) > 0 {
		x.b.Encrypt(dst, src[:x.blockSize])
		src = src[x.blockSize:]
		dst = dst[x.blockSize:]
	}
}

type ECBDecrypter ECB

// NewECBDecrypter returns a BlockMode which decrypts in electronic code book
// mode, using the given Block.
func NewECBDecrypter(b cipher.Block) cipher.BlockMode {
	return (*ECBDecrypter)(NewECB(b))
}
func (x *ECBDecrypter) BlockSize() int {
	return x.blockSize
}
func (x *ECBDecrypter) CryptBlocks(dst, src []byte) {
	if len(src)%x.blockSize != 0 {
		fmt.Println(x.blockSize)
		panic("crypto/cipher: input not full blocks")
	}
	if len(dst) < len(src) {
		panic("crypto/cipher: output smaller than input")
	}
	for len(src) > 0 {
		x.b.Decrypt(dst, src[:x.blockSize])
		src = src[x.blockSize:]
		dst = dst[x.blockSize:]
	}
}

// PKCS5Padding _
func PKCS5Padding(ciphertext []byte, blockSize int) []byte {
	padding := blockSize - len(ciphertext)%blockSize
	padtext := bytes.Repeat([]byte{byte(0)}, padding)
	fmt.Println(padding)
	return append(ciphertext, padtext...)
}

// PKCS5UnPadding _
func PKCS5UnPadding(origData []byte) []byte {
	length := len(origData)
	// 去掉最后一个字节 unpadding 次
	index := 0
	for i := length - 1; i >= 0; i-- {
		if origData[i] == 0 {
			index = i
		}
	}
	if index == 0 {
		return origData
	}
	return origData[0:index]
}
